#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @Author  : 张亦辰
# @Copyright (c) Copyright 2025 张亦辰，All rights reserved。
# @File    : win_main.py
# @Software: PyCharm
# @Time    : 2025/2/3 20:22

"""
程序说明：
棋子类，主要用来实现棋子的走棋规则，判断是否可走
"""
from PySide6.QtWidgets import QPushButton

import src.board.constants as const


def exec_piece(class_name, player, c, r):
    """
    动态创建棋子
    :param class_name:  类名
    :param player: 黑红方
    :param c:  棋子所在的列
    :param r:  棋子所在的行
    :return: 棋子实例
    """
    return eval(f"{class_name}('{player}',{c},{r})")


def list_piecesto_arr(pieces_list):
    """
    转换为棋子数组
    """
    arr = [[0 for i in range(10)] for j in range(9)]
    for i in range(0, 9):
        for j in range(0, 10):
            if len(list(filter(lambda cm: cm.c == i and cm.r == j and cm.player == const.playerRed,
                               pieces_list))):
                arr[i][j] = const.playerRed
            elif len(list(filter(lambda cm: cm.c == i and cm.r == j and cm.player == const.playerBlack,
                                 pieces_list))):
                arr[i][j] = const.playerBlack

    return arr


class Pieces(QPushButton):
    """
    棋子类的父类
    """
    # 在棋盘上的行
    r: int = -1
    # 在棋盘上的列
    c: int = -1
    # 图片文件名，中文名,fen协议中棋子代号
    imgKey: str = ""
    # 中文名
    cnName: str = ""
    # fen协议中棋子代号，黑红方以大小写区分
    fenCode: str = ""
    # 黑红方
    player: str = ""
    # 棋子的子力价值
    scoreWeight: int = 0
    # 棋子创建时，在列表中的下标
    ind: int = 0

    def __init__(self, player, c, r, parent=None):
        super(Pieces, self).__init__(parent)
        # 黑红方
        self.player = player
        # 在棋盘上的行
        self.r = r
        # 在棋盘上的列
        self.c = c
        # 图片文件名，中文名,fen协议中棋子代号
        self.imgKey, self.cnName, self.fenCode = self.get_img_key()

    def canmove(self, arr, moveto_c, moveto_r):
        """
        走步是否合规
        :param arr: 棋盘数组
        :param moveto_c: 目标列
        :param moveto_r: 目标行
        :return:
        """
        pass

    def _can_move(self, arr, moveto_c, moveto_r):
        """
        将和士的校验方法
        :param arr:
        :param moveto_c:
        :param moveto_r:
        :return:
        """
        flag = True
        if self.c == moveto_c and self.r == moveto_r:
            flag = False
        if arr[moveto_c][moveto_r] == self.player:
            flag = False
        if moveto_c < 3 or moveto_c > 5:
            flag = False
        if 2 < moveto_r < 7:
            flag = False
        move_x = moveto_c - self.c
        move_y = moveto_r - self.r
        return flag, move_x, move_y

    def get_possible_steps(self, arr: list[list[int]]):
        """
        获取该子可以走的所有步子
        :return:  可走的步子的列表(行，列)
        """
        pass

    def get_next_steps_tip(self, arr: list[list[int]]):
        """
        获取当前子可以走的步子坐标
        :param arr: 当前棋盘的局面
        :return: 可以走的位置列表（行，列）
        """
        result: list[(int, int)] = []
        possible_steps = self.get_possible_steps(arr)
        if possible_steps is not None:
            for item in possible_steps:
                r = item[0]
                c = item[1]
                if self.canmove(arr, c, r):
                    result.append((r, c))
        return result

    def get_img_key(self):
        """
        获取图片的文件名和中文名
        :return:
        """
        # 获取子类的类名加玩家，组成key
        key = self.player + self.__class__.__name__
        attr = const.pieces_map[key]
        return attr[0], attr[1], attr[2]

    def get_score_weight(self, listpieces):
        """
        获取当前子的子力价值（评分）
        :param listpieces:
        :return:
        """
        return None

    def set_pos_board(self, c, r):
        # 在棋盘上的行
        self.r = r
        # 在棋盘上的列
        self.c = c


class Rook(Pieces):
    """
    车
    """

    def __init__(self, player, c, r):
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        if self.c == moveto_c and self.r == moveto_r:
            return False
        if arr[moveto_c][moveto_r] == self.player:
            return False
        if self.c == moveto_c:
            step = -1 if self.r > moveto_r else 1
            for i in range(self.r + step, moveto_r, step):
                if arr[self.c][i] != 0:
                    return False
            # print(" move y")
            return True

        if self.r == moveto_r:
            step = -1 if self.c > moveto_c else 1
            for i in range(self.c + step, moveto_c, step):
                if arr[i][self.r] != 0:
                    return False
            return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        # 垂直向上检查：从车的当前位置开始，向上逐行检查。如果该位置没有棋子（值为 0），
        # 则将该位置添加到结果列表中；如果遇到有棋子的位置，检查该棋子是否为敌方棋子，
        # 如果是则将该位置添加到结果列表中，然后停止检查。
        for r in range(self.r - 1, -1, -1):
            if arr[self.c][r] == 0:
                result.append((r, self.c))
            else:
                if arr[self.c][r] != self.player:
                    result.append((r, self.c))
                break
        # 垂直向下检查：从车的当前位置开始，向下逐行检查，逻辑与垂直向上检查相同。
        for r in range(self.r + 1, 10):
            if arr[self.c][r] == 0:
                result.append((r, self.c))
            else:
                if arr[self.c][r] != self.player:
                    result.append((r, self.c))
                break
        # 水平向左检查：从车的当前位置开始，向左逐列检查，逻辑与垂直检查相同。
        for c in range(self.c - 1, -1, -1):
            if arr[c][self.r] == 0:
                result.append((self.r, c))
            else:
                if arr[c][self.r] != self.player:
                    result.append((self.r, c))
                break
        # 水平向右检查：从车的当前位置开始，向右逐列检查，逻辑与垂直检查相同。
        for c in range(self.c + 1, 9):
            if arr[c][self.r] == 0:
                result.append((self.r, c))
            else:
                if arr[c][self.r] != self.player:
                    result.append((self.r, c))
                break

        return result

    def get_score_weight(self, listpieces):
        score = 11
        return score


class Knight(Pieces):
    """
    马
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        if self.c == moveto_c and self.r == moveto_r:
            return False
        if arr[moveto_c][moveto_r] == self.player:
            return False
        # print(str(self.x) +""+str(self.y))
        move_x = moveto_c - self.c
        move_y = moveto_r - self.r
        if abs(move_x) == 1 and abs(move_y) == 2:
            step = 1 if move_y > 0 else -1
            if arr[self.c][self.r + step] == 0:
                return True
        if abs(move_x) == 2 and abs(move_y) == 1:
            step = 1 if move_x > 0 else -1
            if arr[self.c + step][self.r] == 0:
                return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        # 横向的四个坐标点，从左到右
        moveto_cs = self.__calculate_moves(self.c, 9)
        # 纵向4个坐标点，从上到下
        moveto_rs = self.__calculate_moves(self.r, 10)

        # 找出有效的马步的行列组合，即
        # moveto_cs的第0、3项，分别和 moveto_rs 第1、2项组合
        # moveto_cs的第1、2项，分别和 moveto_rs 第0、3项组合，如下图
        # moveto_cs  0  1  2  3
        #             \/````\/
        #             /\____/\
        # moveto_rs  0  1  2  1

        for i in range(4):
            col = moveto_cs[i]
            if 0 <= col <= 8:
                if 1 <= i <= 2:
                    start, step = 0, 3
                else:
                    start, step = 1, 1
                for j in range(start, start + step + 1, step):
                    row = moveto_rs[j]
                    if 0 <= row <= 9:
                        result.append((row, col))
        return result

    def __calculate_moves(self, position, limit):
        """
        计算一个方向上可能的移动位置
        :param position: 当前位置
        :param limit: 该方向的边界限制
        :return: 可能的移动位置列表
        """
        moves = [-1, -1, -1, -1]
        # 如果马当前位置距离边界至少有 2 个格子，那么马可以向左（或向上）移动 2 个格子，
        # 将 moves[0] 更新为 position - 2
        if position >= 2:
            moves[0] = position - 2
        # 如果马当前位置距离边界至少有 1 个格子，那么马可以向左（或向上）移动 1 个格子，
        # 将 moves[1] 更新为 position - 1
        if position >= 1:
            moves[1] = position - 1
        # 如果马当前位置距离另一侧边界至少有 1 个格子，那么马可以向右（或向下）移动 1 个格子，
        # 将 moves[2] 更新为 position + 1
        if position < limit - 1:
            moves[2] = position + 1
        # 如果马当前位置距离另一侧边界至少有 2 个格子，那么马可以向右（或向下）移动 2 个格子，
        # 将 moves[3] 更新为 position + 2
        if position < limit - 2:
            moves[3] = position + 2

        return moves

    def get_score_weight(self, listpieces):
        score = 5
        return score


class Bishop(Pieces):
    """
    象
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        if self.c == moveto_c and self.r == moveto_r:
            return False
        if arr[moveto_c][moveto_r] == self.player:
            return False
        if self.r <= 4 and moveto_r >= 5 or self.r >= 5 and moveto_r <= 4:
            return False
        move_x = moveto_c - self.c
        move_y = moveto_r - self.r
        if abs(move_x) == 2 and abs(move_y) == 2:
            step_x = 1 if move_x > 0 else -1
            step_y = 1 if move_y > 0 else -1
            if arr[self.c + step_x][self.r + step_y] == 0:
                return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        # 定义象移动的方向偏移量
        directions = [(-2, -2), (-2, 2), (2, -2), (2, 2)]
        # 遍历所有可能的移动方向
        for dr, dc in directions:
            new_r = self.r + dr
            new_c = self.c + dc
            # 检查新位置是否在棋盘范围内
            if 0 <= new_r <= 9 and 0 <= new_c <= 8:
                # 检查是否在同一边
                if (self.r <= 4 and new_r <= 4) or (self.r >= 5 and new_r >= 5):
                    # 检查象眼是否有棋子
                    eye_r = self.r + dr // 2
                    eye_c = self.c + dc // 2
                    if arr[eye_c][eye_r] == 0:
                        result.append((new_r, new_c))
        return result

    def get_score_weight(self, listpieces):
        score = 2
        return score


class Advisor(Pieces):
    """
    仕
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        # if self.c == moveto_c and self.r == moveto_r:
        #     return False
        # if arr[moveto_c][moveto_r] == self.player:
        #     return False
        # if moveto_c < 3 or moveto_c > 5:
        #     return False
        # if 2 < moveto_r < 7:
        #     return False
        # move_x = moveto_c - self.c
        # move_y = moveto_r - self.r
        flag, mx, my = self._can_move(arr, moveto_c, moveto_r)
        if not flag:
            return False
        if abs(mx) == 1 and abs(my) == 1:
            return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        # 向左向右移动量，-1不可移动
        moveto_cs = [-1, -1]
        # 向上向下移动量，-1不可移动
        moveto_rs = [-1, -1]
        if self.c > 3:
            moveto_cs[0] = self.c - 1
        if self.c < 5:
            moveto_cs[1] = self.c + 1
        # 根据红黑方，判断纵向可走的步骤
        if self.r > 0 or self.r > 7:
            moveto_rs[0] = self.r - 1
        if self.r < 3 or self.r < 9:
            moveto_rs[1] = self.r + 1

        for i in range(0, 2):
            if moveto_cs[i] > 0:
                for j in range(0, 2):
                    if moveto_rs[j] > 0:
                        result.append((moveto_rs[j], moveto_cs[i]))
        return result

    def get_score_weight(self, listpieces):
        score = 2
        return score


class King(Pieces):
    """
    将
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        # 不能移动到自身位置
        if self.c == moveto_c and self.r == moveto_r:
            return False
        # 不能移动到己方棋子位置
        if arr[moveto_c][moveto_r] == self.player:
            return False
        # 确保移动位置在九宫格内
        if moveto_c < 3 or moveto_c > 5:
            return False
        if 2 < moveto_r < 7:
            return False

        # 检查是否是一步一格的移动
        move_x = moveto_c - self.c
        move_y = moveto_r - self.r
        if abs(move_x) + abs(move_y) != 1:
            return False

        # todo 检查目标位置是否被将
        # enemy_player = const.playerBlack if self.player == const.playerRed else const.playerRed
        # for piece in self.piecesList:
        #     if piece.player == enemy_player:
        #         if piece.canmove(arr, moveto_c, moveto_r):
        #             return False
        return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        if self.c > 3:
            result.append((self.r, self.c - 1))
        if self.c < 5:
            result.append((self.r, self.c + 1))

        if self.player == const.playerRed:
            # 红方的将
            if self.r > 7:
                result.append((self.r - 1, self.c))
            if self.r < 9:
                result.append((self.r + 1, self.c))
        elif self.player == const.playerBlack:
            if self.r > 0:
                result.append((self.r - 1, self.c))
            if self.r < 2:
                result.append((self.r + 1, self.c))
        return result

    def get_score_weight(self, listpieces):
        score = 150
        return score


class Cannon(Pieces):
    """
    砲
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        # 检查是否移动到自身位置，如果是则返回 False
        if self.c == moveto_c and self.r == moveto_r:
            return False
        # 检查目标位置是否有己方棋子，如果有则返回 False
        if arr[moveto_c][moveto_r] == self.player:
            return False

        # 检查垂直移动
        if self.c == moveto_c:
            return self.__check_move( self.r, moveto_r, lambda i: arr[self.c][i])
        # 检查水平移动
        if self.r == moveto_r:
            return self.__check_move( self.c, moveto_c, lambda i: arr[i][self.r])

        return False

    def __check_move(self, start, end, get_cell: callable):
        """
        辅助函数，用于检查移动路径上的棋子情况
        :param start: 起始位置
        :param end: 目标位置
        :param get_cell: 获取指定位置的棋子信息的函数
        :return: 是否可以移动
        """
        # 确定移动方向，反向移动 step 为 -1，正向移动 step 为 1
        step = -1 if start > end else 1
        # 初始化一个标志变量，用于标记是否已经越过一个棋子,False为没有越过棋子
        over_flag = False
        # 遍历移动路径上的所有位置
        for i in range(start + step, end, step):
            # 如果路径上有棋子
            if get_cell(i) != 0:
                # 如果已经越过一个棋子，返回 False
                if over_flag:
                    return False
                else:
                    # 标记已经越过一个棋子
                    over_flag = True

        # 如果已经越过一个棋子，但目标位置没有棋子，返回 False
        if over_flag and get_cell(end) == 0:
            return False
        # 如果没有越过一个棋子，但目标位置有棋子，返回 False
        if not over_flag and get_cell(end) != 0:
            return False

        return True

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        for i in range(0, 10):
            if self.r != i:
                result.append((i, self.c))
        for j in range(0, 9):
            if self.c != j:
                result.append((self.r, j))
        return result

    def get_score_weight(self, listpieces):
        score = 6
        return score


class Pawn(Pieces):
    """
    兵
    """

    def __init__(self, player, c, r):
        self.player = player
        super().__init__(player, c, r)

    def canmove(self, arr, moveto_c, moveto_r):
        # 检查是否移动到自身位置
        if self.c == moveto_c and self.r == moveto_r:
            return False
        # 检查目标位置是否有己方棋子
        if arr[moveto_c][moveto_r] == self.player:
            return False

        # 计算移动的横向和纵向距离
        move_x = moveto_c - self.c
        move_y = moveto_r - self.r

        # 定义红黑方未过河时的移动规则
        red_uncrossed = self.player == const.playerRed and self.r > 4 and move_x != 0
        black_uncrossed = self.player == const.playerBlack and self.r <= 4 and move_x != 0
        # 定义红黑方移动方向错误规则
        red_wrong_direction = self.player == const.playerRed and move_y > 0
        black_wrong_direction = self.player == const.playerBlack and move_y < 0

        # 检查是否违反红黑方未过河或移动方向错误规则
        if red_uncrossed or black_uncrossed or red_wrong_direction or black_wrong_direction:
            return False

        # 检查是否是一步一格的移动
        return abs(move_x) + abs(move_y) == 1

    def get_possible_steps(self, arr: list[list[int]]):
        result = []
        # 根据兵（卒）所属的玩家（红方或黑方）和当前所在的行位置，确定其可能的移动方向。
        # 红方：
        # 若兵（卒）未过河（self.r > 4），只能向前移动一格，移动方向为(-1, 0)。
        # 若兵（卒）已过河（self.r <= 4），除了向前移动一格，还能向左或向右移动一格，
        # 移动方向为[(-1, 0), (0, -1), (0, 1)]。
        # 黑方：
        # 若兵（卒）未过河（self.r <= 4），只能向前移动一格，移动方向为(1, 0)。
        # 若兵（卒）已过河（self.r > 4），除了向前移动一格，还能向左或向右移动一格，
        # 移动方向为[(1, 0), (0, -1), (0, 1)]。
        if self.player == const.playerRed:
            directions = [(-1, 0)] if self.r > 4 else [(-1, 0), (0, -1), (0, 1)]
        else:  # const.playerBlack
            directions = [(1, 0)] if self.r <= 4 else [(1, 0), (0, -1), (0, 1)]

        # 遍历所有可能的移动方向
        for dr, dc in directions:
            new_r = self.r + dr
            new_c = self.c + dc
            # 检查新位置是否在棋盘范围内
            if 0 <= new_r <= 9 and 0 <= new_c <= 8:
                result.append((new_r, new_c))

        return result

    def get_score_weight(self, listpieces):
        score = 2
        return score
